#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
AI-MindCare, Multi-Agent System for Mental Health Support
Copyright (C) 2025  AI-MindCare Team
"""

#-----------------------------------------------------------------------------------------------------------------------
"""Python标准库"""
import os
import re
import io
import sys
import uuid
import time
import json
import random
from datetime import timezone, timedelta
import threading
"""第三方库"""
import boto3
import redis
import bcrypt
import logging
import urllib.request
from celery import Celery
from dotenv import load_dotenv
from botocore.client import Config
from datetime import datetime, timedelta
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_jwt_extended import get_jwt
from flask import Flask, render_template, request, jsonify, session as flask_session
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_required, get_jwt_identity, JWTManager
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest
"""本地库"""
try:
    from agents.companion import CompanionAgent
    from utils.orchestrator import Orchestrator
    from utils.memory_system import MemorySystem
    from utils.llm_service import LLMService
    from config.settings import SETTINGS
except ImportError as e:
    logging.critical(f"Web App 无法导入核心组件: {e}", exc_info=True)
    raise SystemExit(f"Web App 启动失败: {e}")
from models import db, User, MoodRecord
# UserProfile, Conversation, Message, LongTermMemory,
# from utils.sms import send_sms

# from utils.emails import send_email
"""加载环境变量"""
load_dotenv()
#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""配置日志"""
logging.basicConfig(
    # filename="logs/mindcare_system_web.log",  # 新增日志文件路径
    level=logging.INFO,  # 可以根据 SETTINGS.debug_mode 调整
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler("mindcare_system_web.log")
    ],
    encoding='utf-8'  # 新增编码设置
)
# 创建日志记录器
logger = logging.getLogger("AI-MindCare-System-Web")
# 设置标准输出和标准错误的编码为UTF-8
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8')  #改变标准输出的默认编码
# 清除原有可能使用错误编码的handler
for handler in logger.handlers[:]:
    logger.removeHandler(handler)
# 添加UTF-8编码的handler
handler = logging.StreamHandler(stream=sys.stdout)
handler.setFormatter(
    logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                      datefmt='%Y-%m-%d %H:%M:%S'))
handler.encoding = 'utf-8'  # 明确指定编码为UTF-8
logger.addHandler(handler)
logger.setLevel(logging.INFO)
#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""初始化 Flask 实例"""
app = Flask(__name__)
CORS(app, supports_credentials=True)  # 支持跨域请求
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv(
    "DATABASE_URL")  # 从配置文件中读取数据库URL
app.config["JWT_SECRET_KEY"] = os.getenv("JWT_SECRET_KEY")
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(hours=2)
jwt = JWTManager(app)
db.init_app(app)

# 配置 Flask session 密钥，用于加密会话数据# !!! 生产环境中必须使用强随机密钥，并从配置或环境变量读取 !!!
app.secret_key = SETTINGS.get("FLASK_SECRET_KEY",
                              os.urandom(24))  # SETTINGS是全局配置实例，供其他模块使用
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2)  # 设置会话超时时间
app.config['SESSION_COOKIE_HTTPONLY'] = True  # 防止会话cookie被JS读取
app.config[
    'SESSION_COOKIE_SECURE'] = not SETTINGS.debug_mode  # 强制HTTPS传输cookie（生产环境启用）
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'  # 防止CSRF
#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""配置 S3 客户端"""
s3_client = boto3.client(
    's3',
    aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
    aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
    endpoint_url=os.getenv("AWS_ENDPOINT_URL"),
    config=Config(signature_version='s3v4'),
    region_name=os.getenv("AWS_REGION"))
""" 配置redis """
redis_client = redis.Redis(
    host=os.getenv("REDIS_HOST", "localhost"),
    port=os.getenv("REDIS_PORT", 6379),
    # password=os.getenv("REDIS_PASSWORD", ""),
    db=0)
"""配置Celery"""
celery = Celery("tasks", broker="redis://localhost:6379/0")
"""配置限流"""
MAX_ATTEMPTS = 5
BLOCK_TIME = 300
#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
# Token黑名单管理
jwt_blacklist = redis_client


@jwt.token_in_blocklist_loader
def check_if_token_in_blacklist(jwt_header, jwt_payload):
    jti = jwt_payload["jti"]
    return jwt_blacklist.exists(f"blacklist:{jti}")


# 会话不活跃监控
# 会话不活跃监控
def start_inactivity_monitor():

    def monitor_loop():
        logger.info("启动会话不活跃监控线程")
        while True:
            try:
                time.sleep(60)  # 每分钟检查一次
                current_time = datetime.now()

                # 获取所有活跃用户
                user_keys = redis_client.keys("user:active:*")
                for key in user_keys:
                    user_id = key.decode().split(":")[2]
                    last_active = datetime.fromisoformat(
                        redis_client.get(key).decode())

                    # 如果用户超过30分钟不活跃，触发记忆更新
                    if (current_time - last_active).total_seconds() > 1800:
                        update_long_term_memory.delay(user_id,
                                                      "session_timeout")
                        redis_client.delete(key)  # 移除不活跃标记
            except Exception as e:
                logger.error(f"会话监控线程错误: {str(e)}", exc_info=True)
                time.sleep(60)  # 出错后等待1分钟再重试

    # 启动守护线程
    monitor_thread = threading.Thread(target=monitor_loop, daemon=True)
    monitor_thread.start()


# 长期记忆更新任务
# 长期记忆更新任务
@celery.task
def update_long_term_memory(user_id, trigger_type="auto"):
    try:
        logger.info(f"开始更新用户{user_id}的长期记忆，触发类型：{trigger_type}")

        # 生成长期记忆
        long_term_memory = generate_long_term_memory(user_id)

        if long_term_memory:
            # 更新到内存系统
            mindcare_system["memory_system"]._update_long_term_memory(
                user_id, long_term_memory)
            logger.info(f"用户{user_id}长期记忆更新成功")

            # # 同步到数据库# 这一步先不实现
            # try:
            #     with app.app_context():
            #         memory_record = LongTermMemory.query.filter_by(user_id=user_id).first()
            #         if not memory_record:
            #             memory_record = LongTermMemory(user_id=user_id)
            #             db.session.add(memory_record)

            #         memory_record.long_term_memory = json.dumps(long_term_memory)
            #         db.session.commit()
            # except Exception as db_e:
            #     logger.error(f"更新数据库长期记忆失败: {str(db_e)}", exc_info=True)

    except Exception as e:
        logger.error(f"更新用户{user_id}记忆失败：{str(e)}", exc_info=True)


# Celery Beat 配置 - 每日自动更新
celery.conf.beat_schedule = {
    "daily-memory-update": {
        "task": "app.update_long_term_memory",
        "schedule": timedelta(hours=24),
        "args": (None, "daily_update")  # None表示更新所有用户，实际实现中应遍历用户列表
    }
}

#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""初始化核心系统"""
mindcare_system = {}  # 全局变量存储系统实例
user_buffers = {}  # 用于存储每个用户的会话缓冲区，字典类型，键为用户ID，值为会话缓冲区列表


def init_mindcare_system():
    """初始化 AI-MindCare System 的所有组件"""
    global mindcare_system, user_buffers
    try:
        logger.info("开始初始化 AI-MindCare 核心系统...")
        # 确保 LLM 配置有效
        if not SETTINGS.validate_llm_config():  # 验证LLM配置是否有效
            logger.warning("LLM 配置不完整，功能可能受限。")
            # 可以选择是否继续，或者抛出错误
            # raise ValueError("LLM 配置无效，无法启动系统。")
        # 创建实例并初始化
        llm_service_instance = LLMService(settings=SETTINGS)  # 创建实例
        memory_system_instance = MemorySystem(
            persistence_path=SETTINGS.data_dir /
            "mindcare_memory_web.json",  # 为web应用使用单独的内存文件
            llm_service=llm_service_instance  # 传递给 MemorySystem 用于归纳
        )
        user_buffers = memory_system_instance._get_user_buffers()  # 初始化用户会话缓冲区

        companion_instance = CompanionAgent(  # 陪伴智能体实例
            memory_system=memory_system_instance,
            llm_service=llm_service_instance)
        orchestrator_instance = Orchestrator(  # 编排器实例
            companion=companion_instance,
            memory_system=memory_system_instance)

        mindcare_system = {
            "companion": companion_instance,
            "orchestrator": orchestrator_instance,  # 编排器组件
            "memory_system": memory_system_instance,
            "user_buffers": user_buffers,  # 用户会话缓冲区
            "llm_service": llm_service_instance
        }
        logger.info("AI-MindCare System Successfully Initialized!")
        start_inactivity_monitor()  # 启动会话监控流程
        return True
    except Exception as e:
        logger.critical(
            f"Failed to initialize the AI-MindCare System: {str(e)}",
            exc_info=True)
        mindcare_system = {}  # 清空以表示失败
        return False


if not init_mindcare_system():  # 在 Flask Web 应用正式启动前，预先检查核心系统的初始化状态，确保基础服务可用
    logger.critical("无法初始化核心系统，Web 应用可能无法正常工作。")
    # 可以选择退出应用或让其以受限模式运行
    # raise SystemExit("核心系统初始化失败，Web 应用无法启动。")

#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""会话模块"""


def load_long_term_memory(user_id):  # 从外部文件或数据库加载长期记忆
    # 这里为了方便，选择从json文件中加载
    return mindcare_system["memory_system"]._load_long_term_memory(
        user_id)  # 字典类型


"""更新用户长期记忆"""


def should_update_long_term_memory(user_id, conversation_id):
    summary = generate_summary(user_id, conversation_id)  # 为某个缓冲区会话生成摘要
    long_term_memory = mindcare_system["memory_system"]._load_long_term_memory(
        user_id)  # 先加载长期记忆
    if not long_term_memory:  # 如果长期记忆为空，直接返回
        long_term_memory = generate_long_term_memory(user_id)  # 尝试生成新的长期记忆
        mindcare_system["memory_system"]._update_long_term_memory(
            user_id, long_term_memory)  #
    memory_text = "\\n\\n".join(long_term_memory)  # 使用原始字符串

    prompt = f"""请基于以下长期记忆和新的会话摘要，判断新的摘要内容是否显著不同或重要到需要更新长期记忆。
    如果是则请基于现有长期记忆和新摘要生成一个新的长期记忆，以 JSON 字典格式输出，内容需涵盖所有摘要的关键信息，例如来访者的性格、提出的核心困扰、咨询师采用的干预方法、达成的阶段性结论等。
    输出示例: {{"性格": "内向", "核心困扰": "工作压力大", "干预方法": "建议调整工作时间和心态", "阶段性结论": "来访者决定尝试调整"}}
    用户长期记忆:{memory_text};新的会话摘要: {summary}"""
    response = mindcare_system["llm_service"].generate_response(
        prompt=prompt,
        system_message="你是一个专业的心理咨询记录总结评估系统，需根据对话内容判断是否需要更新长期记忆。")
    mindcare_system["memory_system"]._update_long_term_memory(
        user_id, json.loads(response))  # 更新长期记忆
    # memory.long_term_memory = response
    # db.session.commit()
    logger.info(f"用户 {user_id} 的长期记忆已更新。")
    # 同步更新 Flask session 中的长期记忆
    # flask_session['memory'] = response
    # return response  # 返回更新后的长期记忆


def generate_long_term_memory(user_id):  # 基于会话记录生成长期记忆
    try:
        # 获取用户的所有会话
        conversations = mindcare_system[
            "memory_system"]._get_user_conversation_history(
                user_id)  # 从内存系统获取用户的所有会话

        # conversations = Conversation.query.filter_by(user_id=user_id).all()
        if not conversations:
            logger.info(f"用户 {user_id} 没有会话记录，无法生成长期记忆。")
            return {}

        else:
            # 组合所有摘要生成长期记忆提示
            prompt = f"""请根据以下历史会话生成一个全面的长期记忆，以 JSON 字典格式输出，内容需涵盖所有摘要的关键信息，例如来访者的性格、提出的核心困扰、咨询师采用的干预方法、达成的阶段性结论等。
    输出示例: {{"性格": "内向", "核心困扰": "工作压力大", "干预方法": "建议调整工作时间和心态", "阶段性结论": "来访者决定尝试调整"}}
    历史会话如下：{",".join(conversations)}"""

            # 调用大模型生成长期记忆
            long_term_memory = mindcare_system["llm_service"].generate_response(
                prompt=prompt,
                system_message=
                "你是一个专业的心理咨询长期记忆生成系统，需根据多个会话摘要生成逻辑清晰、全面准确的长期记忆，以 JSON 字典格式输出。",
                max_tokens=1200,
                temperature=0.7)

            logger.info(f"成功为用户 {user_id} 生成长期记忆。长期记忆内容: {long_term_memory}")

            import re
            json_match = re.search(r'```json\s*([\s\S]*?)\s*```',
                                   long_term_memory)
            if json_match:
                long_term_memory = json_match.group(1)
            else:
                # 尝试直接解析，可能LLM没有包含代码块标记
                logger.warning(f"未找到JSON代码块标记，尝试直接解析: {long_term_memory}")

            # 预处理：修复引号转义问题
            # 查找属性名和值中的双引号，确保它们被正确转义
            def fix_escapes(match):
                full_match = match.group(0)
                content = match.group(1)
                # 替换内容中的双引号，但不替换属性名中的双引号
                escaped_content = content.replace('"', '\\"')
                return f'"{escaped_content}"'

            # 修复属性值中的双引号
            long_term_memory = re.sub(r'"([^"\\]*(\\.[^"\\]*)*)"', fix_escapes,
                                      long_term_memory)

            # 解析JSON
            return json.loads(long_term_memory)

    except json.JSONDecodeError as e:
        logger.error(f"解析长期记忆JSON时发生错误: {e}. 原始内容: {long_term_memory}",
                     exc_info=True)
        return {}
    except Exception as e:
        logger.error(f"为用户 {user_id} 生成长期记忆时发生错误: {e}", exc_info=True)
        return {}


@app.route('/protected')
def protected():
    if 'user_id' in flask_session:
        memory = flask_session.get('memory')
        return f'Your long term memory: {memory}'
    return 'Please log in'


def generate_summary(user_id, conversation_id):
    try:
        # 获取用户的对话缓冲区
        user_buffer = user_buffers.get(user_id, {}).get(conversation_id, [])
        if not user_buffer:
            logging.info(f"用户 {user_id} 的对话 {conversation_id} 缓冲区为空，无法生成摘要。")
            return None

        # 构建提示词
        prompt = """
请对以下心理咨询对话进行总结，内容需包含：
1. 来访者提出的核心困扰或问题；
2. 咨询师采用的主要干预方法或给出的建议；
3. 对话中达成的阶段性结论或下一步计划。

对话内容如下：
""" + "\n".join([f"{msg['role']}: {msg['content']}" for msg in user_buffer])

        # 生成摘要
        summary = mindcare_system["llm_service"].generate_response(
            prompt=prompt,
            system_message="你是一个专业的心理咨询记录摘要生成系统，需根据对话内容生成逻辑清晰、重点突出的摘要。",
            max_tokens=800,
            temperature=0.7)
        logging.info(f"成功为用户 {user_id} 的对话 {conversation_id} 生成摘要。")
        return summary
    except Exception as e:
        logging.error(f"生成摘要时发生错误: {e}", exc_info=True)
        return None


@app.route('/api/start_conversation', methods=['POST'])
@jwt_required()  # 添加 JWT 验证装饰器，确保只有已登录用户才能启动咨询
def start_conversation():
    """开始新的会话（由前端调用）"""
    logger.info(f"开始新的 Web 会话，来自: {request.remote_addr}")
    logger.info(f"JWT 有效载荷: {get_jwt_identity()}")
    # 从 JWT 获取用户身份
    user_id = get_jwt_identity()

    if not user_id:
        return jsonify({
            "status": "error",
            "message": "请先登录"
        }), 401  # 前端需要跳转至登录页
    redis_client.setex(f"user:active:{user_id}", 1800,
                       datetime.now().isoformat())
    if not mindcare_system:
        return jsonify({"status": "error", "message": "系统未初始化，请稍后重试。"}), 503
    try:
        # 调用 Orchestrator 获取初始信息
        orchestrator = mindcare_system.get("orchestrator")  # 编排器
        if not orchestrator:
            return jsonify({"status": "error", "message": "编排器服务不可用。"}), 503

        # 启动新的咨询会话，获取 ID 和初始消息
        conversation_id, initial_message = orchestrator.start_new_web_conversation(
        )
        # 这里启动了一个新的咨询会话会显示在前端，更新
        user_id = get_jwt_identity()  # 从 JWT 获取用户ID

        # 将 conversation_id 存储在 Flask session 中
        flask_session['conversation_id'] = conversation_id
        flask_session.permanent = True  # 使用配置的超时时间
        logger.info(
            f"为新 Web 会话 {request.remote_addr} 启动咨询，ID: {conversation_id}")
        mindcare_system["memory_system"].create_conversation_context(
            user_id, conversation_id, {})

        return jsonify({
            "status": "success",
            "conversation_id": conversation_id,  # 返回 ID 给前端（虽然前端可能不再直接使用它）
            "initial_message": {  # 返回初始消息让前端显示
                "agent_type": "receptionist",
                "message": initial_message
            }
        })

    except Exception as e:
        logger.error(f"开始 Web 咨询失败: {str(e)}", exc_info=True)
        return jsonify({"status": "error", "message": "开始咨询时发生服务器内部错误。"}), 500


@app.route('/api/conversation', methods=['POST'])
@jwt_required()  # 新增JWT验证
def handle_conversation():
    """处理来自前端的对话消息"""

    user_id = get_jwt_identity()

    if not user_id:
        return jsonify({"status": "error", "message": "请先登录"})
    # if user_id not in flask_session:
    #     return jsonify({"status": "error", "message": "请先登录"}), 401
    redis_client.setex(f"user:active:{user_id}", 1800,
                       datetime.now().isoformat())
    if not mindcare_system:
        return jsonify({"status": "error", "message": "系统未初始化，请稍后重试。"}), 503

    try:
        try:
            data = request.get_json(
                force=True, silent=False
            )  # 强制解析 JSON 数据（即使没有 Content-Type: application/json）
            if not data:  # 检查数据是否为空
                return jsonify({
                    "status": "error",
                    "message": "无效的JSON格式"
                }), 400
        except Exception as e:  # 捕获解析错误
            logger.error(f"解析请求数据失败: {str(e)}", exc_info=True)
            return jsonify({
                "status": "error",
                "message": f"解析请求数据失败: {str(e)}"
            }), 400

        # 在这个函数内部需要更新缓冲区和长期记忆

        user_message = data.get('message')
        logger.info(f"Web 消息接收: {user_message}")  # 记录消息内容，用于调试和日志记录
        # 优先从 Flask session 获取 conversation_id
        conversation_id = flask_session.get('conversation_id')
        # 如果 session 中没有，尝试从请求中获取（作为后备或调试）
        if not conversation_id:
            conversation_id = data.get('conversation_id')
            logger.warning(
                f"无法从 session 获取 conversation_id，尝试从请求体获取: {conversation_id}")

        if not conversation_id:
            return jsonify({
                "status": "error",
                "message": "会话ID丢失，请刷新页面开始新的咨询。"
            }), 400
        if not user_message:
            return jsonify({"status": "error", "message": "发送的消息不能为空。"}), 400

        # 获取 Orchestrator编排器 实例
        orchestrator = mindcare_system.get("orchestrator")
        if not orchestrator:
            return jsonify({"status": "error", "message": "编排器服务不可用。"}), 503

        # 调用 Orchestrator 处理消息

        if user_id not in user_buffers:
            user_buffers[user_id] = {}
        if conversation_id not in user_buffers[user_id]:
            user_buffers[user_id][conversation_id] = []
        # 添加用户消息转成字典到缓冲区

        user_buffers[user_id][conversation_id].append({
            "role":
            "user",
            "content":
            user_message,
            "timestamp":
            datetime.now().isoformat()
        })  # 更新会话缓冲区

        # mindcare_system["memory_system"].create_conversation_context(, {})

        user_buffer = user_buffers[user_id][conversation_id]  # 加载用户的会话缓冲区
        long_term_memory = load_long_term_memory(user_id=user_id)
        if not long_term_memory:  # 如果长期记忆不存在，尝试生成
            long_term_memory = generate_long_term_memory(user_id)
            mindcare_system["memory_system"]._update_long_term_memory(
                user_id, long_term_memory)  # 更新长期记忆
        logger.info(
            f"用户 {user_id} 的长期记忆: {long_term_memory}，id={conversation_id}")
        logger.info(f"{user_buffers[user_id][conversation_id]}")  # 打印用户的会话缓冲区
        response_data = orchestrator.handle_web_message(
            user_id, conversation_id, user_message, user_buffer,
            long_term_memory)

        # 添加AI响应到缓冲区
        if response_data.get("status") == "success":
            user_buffers[user_id][consulation_id].append({
                "role":
                response_data.get('agent_type'),
                "content":
                response_data.get("message"),
                "timestamp":
                datetime.now().isoformat()  # 记录时间戳 
            })
        if len(user_buffers[user_id][conversation_id]) > 20:  # 保留最近的20条消息
            # summary = generate_summary(user_id, conversation_id)  # 生成摘要
            should_update_long_term_memory(user_id, conversation_id)  # 更新长期记忆
            user_buffers[user_id][conversation_id] = []  # 清空缓冲区

        # 如果 Orchestrator 返回错误状态，直接返回
        if response_data.get("status") == "error":
            return jsonify(response_data), 500  # 或根据错误类型返回不同状态码

        return jsonify(response_data)
    except Exception as e:
        logger.error(
            f"处理 Web 对话失败 (Session Consult ID: {flask_session.get('conversation_id')}): {str(e)}",
            exc_info=True)
        return jsonify({
            "status": "error",
            "message": "处理您的消息时发生服务器内部错误。"
        }), 500


@app.route('/api/logout', methods=['POST'])
@jwt_required()
def logout():
    try:
        # 新增：触发长期记忆更新
        jti = get_jwt()["jti"]
        user_id = get_jwt_identity()

        # 获取 JWT 过期时间（timedelta 对象）
        token_expires = app.config["JWT_ACCESS_TOKEN_EXPIRES"]
        # 转换为整数秒数
        expires_seconds = int(token_expires.total_seconds())

        # 将 Token 加入黑名单（确保时间参数为整数）
        jwt_blacklist.setex(f"blacklist:{jti}", expires_seconds, "true")
        logger.info(
            f"用户 {user_id} 的Token {jti} 已加入黑名单，过期时间：{expires_seconds} 秒")

        # 触发长期记忆更新
        update_long_term_memory.delay(user_id, "manual_logout")

        return jsonify(msg="Successfully logged out")
    except Exception as e:
        logger.error(f"退出登录失败: {str(e)}", exc_info=True)
        return jsonify({"status": "error", "message": "退出登录时发生错误，请稍后重试"}), 500


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""渲染主页"""


@app.route('/')
def index():
    # flask_session.clear()  # 清理旧的会话数据（如果需要）
    return 'Hi~, This is  AI-MindCare System!'


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""查看Session"""


@app.route('/debug/session')
def debug_session():
    # 将 flask_session 中的内容转换为字典后返回
    return jsonify(dict(flask_session))


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""发送手机验证码"""


@celery.task
def async_send_sms(phone, code):
    try:
        # send_sms(phone, code)
        pass
    except Exception as e:
        logger.error(f"手机验证码发送失败: {str(e)}", exc_info=True)


@app.route("/api/send_sms", methods=["POST"])
def handle_send_sms():
    phone = request.json.get("phone")

    # 1.限流校验（优先级最高）
    if redis_client.exists(f"sms_code:{phone}"):
        return jsonify({"code": 429, "msg": "当前操作过于频繁，请稍后再试"}), 429

    # 2.校验手机号格式（国内），前端最好也能校验一下
    if not re.match(r"^1[3-9]\d{9}$", phone):
        return jsonify({"code": 400, "msg": "错误的手机号格式"}), 400

    # 3.检查是否已注册（注册时需校验）
    if request.path == "/register" and User.query.filter_by(
            phone=phone).first():
        return jsonify({"code": 400, "msg": "手机号已注册"}), 400
    # 4.限流标记（通过后才记录）
    redis_client.setex(f"sms_code:{phone}", 60, "1")  # 60秒内禁止再次发送

    # 5.生成6位验证码并异步发送
    # code = str(random.randint(100000, 999999))
    code = "666666"  # 先写死，便于测试
    async_send_sms.delay(phone, code)  # 异步调用

    # 6.存储验证码到Redis（有效期5分钟）
    redis_client.setex(f"sms_code:{phone}", 300, code)

    # 7.返回脱敏手机号
    masked_phone = phone[:3] + "****" + phone[7:]
    return jsonify({
        "code": 200,
        "msg": "验证码已发送至手机尾号{}".format(masked_phone[-4:])
    })


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""发送邮箱验证码"""


@celery.task
def async_send_email(phone, code):
    try:
        # send_email(to, subject, body)
        pass
    except Exception as e:
        logger.error(f"邮件发送失败: {str(e)}", exc_info=True)


@app.route("/api/send_email", methods=["POST"])
def handle_send_email():
    email = request.json.get("email")

    # 1.限流校验（优先级最高）
    if redis_client.exists(f"email_code:{email}"):
        return jsonify({"code": 429, "msg": "当前操作过于频繁，请稍后再试"}), 429
    # 2. 校验邮箱格式
    if not re.match(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
                    email):
        return jsonify({"code": 400, "msg": "错误的邮箱格式"}), 400

    # 3.检查是否已注册（注册时需校验）
    if request.path == "/register" and User.query.filter_by(
            email=email).first():
        return jsonify({"code": 400, "msg": "邮箱已注册"}), 400

    # 4.限流标记（通过后才记录）
    redis_client.setex(f"email_code:{email}", 60, "1")  # 60秒内禁止再次发送

    # 5.生成6位验证码并异步发送
    # code = str(random.randint(100000, 999999))
    code = "123456"  # 先写死，便于测试
    async_send_email.delay(email, code)  # 异步调用发送邮件任务

    # 6.存储验证码到Redis（有效期5分钟）
    redis_client.setex(f"sms:{email}", 300, code)

    # 7.返回脱敏邮箱
    return jsonify({"code": 200, "msg": "验证码已发送至邮箱{}".format(email)})


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""手机验证码登录"""


@app.route('/api/login_with_phone', methods=['POST'])
def login_with_phone():
    phone = request.json.get('phone')  # 获取手机号
    code = request.json.get('code')  # 获取验证码

    # 校验验证码
    stored_code = redis_client.get(f'sms:{phone}')
    logger.info("手机号{} 验证码对比: stored_code={}, input_code={}".format(
        phone, stored_code, code))  # str.format()
    if not stored_code:
        # 若 stored_code 为 None，可能是验证码过期或未存储
        return jsonify(code=400, msg='验证码已过期或未发送，请重新获取'), 400
    if not stored_code or stored_code.decode() != code:
        return jsonify(code=400, msg='验证码错误'), 400

    # 查询用户是否存在（不存在则自动注册）
    user = User.query.filter_by(phone=phone).first()

    if not user:
        user = User(phone=phone)
        long_term_memory = LongTermMemory(user_id=user.id, long_term_memory="")

        db.session.add(user)
        db.session.add(long_term_memory)  # 添加长期记忆
        db.session.commit()

    memory = load_long_term_memory(user.id)  # 加载长期记忆
    session['memory'] = memory  # 将长期记忆存储在会话中

    # 生成JWT令牌
    access_token = create_access_token(identity=user.id)
    refresh_token: str = create_refresh_token(identity=user.id)

    # 清除已使用的验证码
    redis_client.delete(f'sms_code:{phone}')

    return jsonify(code=200,
                   msg='登录成功',
                   data={
                       'access_token': access_token,
                       'refresh_token': refresh_token
                   })


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""邮箱验证码登录"""


@app.route('/api/login_with_email', methods=['POST'])
def login_with_email():
    email = request.json.get('email')  # 获取邮箱
    code = request.json.get('code')  # 获取验证码

    # 校验邮箱格式
    if not re.match(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
                    email):
        return jsonify(code=400, msg='错误的邮箱格式'), 400

    # 校验验证码
    stored_code = redis_client.get(f'email_code:{email}')
    logger.info("邮箱{} 验证码对比: stored_code={}, input_code={}".format(
        email, stored_code, code))  # str.format()
    if not stored_code:
        # 若 stored_code 为 None，可能是验证码过期或未存储
        return jsonify(code=400, msg='验证码已过期或未发送，请重新获取'), 400
    if not stored_code or stored_code.decode() != code:
        return jsonify(code=400, msg='验证码错误'), 400

    # 查询用户是否存在（不存在则自动注册）
    user = User.query.filter_by(email=email).first()
    logger.info(f"{user}")
    if not user:
        user = User(email=email)
        long_term_memory = LongTermMemory(user_id=user.id, long_term_memory="")

        db.session.add(user)
        db.session.add(long_term_memory)  # 添加长期记忆
        db.session.commit()

    # 生成JWT令牌
    access_token = create_access_token(identity=user.id)
    refresh_token: str = create_refresh_token(identity=user.id)

    # 清除已使用的验证码
    redis_client.delete(f'email_code:{email}')

    return jsonify(code=200,
                   msg='登录成功',
                   data={
                       'access_token': access_token,
                       'refresh_token': refresh_token
                   })


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""设置密码"""


@app.route("/api/register/set_password", methods=["POST"])
@jwt_required()  # 假设注册流程已通过JWT临时令牌验证
def set_password():
    password = request.json.get("password")
    confirm_password = request.json.get("confirm_password")

    logger.info("密码对比: password={}, confirm_password={}".format(
        password, confirm_password))  # str.format()
    user_id = get_jwt_identity()
    print(user_id)
    # 1. 校验两次密码是否一致
    if password != confirm_password:
        return jsonify(code=400, msg="两次输入密码不一致"), 400
    # logger.info("用户id{} 密码对比: password={}, confirm_password={}".format(user_id, password, confirm_password))

    # 2. 密码复杂度校验（示例：至少8位，包含字母和数字）
    if not re.match(r"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$", password):
        return jsonify(code=400, msg="密码需至少8位且包含字母和数字"), 400

    # 3. 生成密码哈希并存储
    user = User.query.get(user_id)
    logger.info("用户id{} 密码哈希: password_hash={}".format(
        user_id, user.password_hash))  # str.format()
    # user.password_hash = bcrypt.generate_password_hash(password).decode("utf-8")
    user.password_hash = bcrypt.hashpw(password.encode(),
                                       bcrypt.gensalt()).decode("utf-8")
    db.session.commit()
    return jsonify(code=200, msg="密码设置成功")


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""账户密码登录"""


@app.route("/api/login/password", methods=["POST"])
def login_with_password():
    phone = request.json.get("phone")
    password = request.json.get("password")

    # 检查是否被锁定
    block_key = f"login_block:{phone}"
    if redis_client.get(block_key):
        return jsonify(code=429, msg="尝试次数过多，请5分钟后再试"), 429

    # 1. 校验手机号是否存在
    user = User.query.filter_by(phone=phone).first()
    if not user:
        return jsonify(code=400, msg="手机号未注册"), 400

    if not bcrypt.checkpw(password.encode(), user.password_hash.encode()):
        # 记录失败次数
        attempt_key = f"login_attempts:{phone}"
        attempts = redis_client.incr(attempt_key)
        redis_client.expire(attempt_key, BLOCK_TIME)

        if attempts >= MAX_ATTEMPTS:
            redis_client.setex(block_key, BLOCK_TIME, "1")

        return jsonify(code=400, msg="密码错误"), 400
    # 3. 生成JWT令牌
    access_token = create_access_token(identity=user.id)
    refresh_token = create_refresh_token(identity=user.id)

    return jsonify(code=200,
                   msg="登录成功",
                   data={
                       "access_token": access_token,
                       "refresh_token": refresh_token
                   })


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""发送手机验证码（用户注册）"""


@app.route('/api/register/send_sms', methods=['POST'])
def send_register_sms():
    phone = request.json.get('phone')

    # 1. 校验手机号格式（中国大陆）
    if not re.match(r'^1[3-9]\d{9}$', phone):
        return jsonify(code=400, msg='手机号格式错误'), 400

    # 2. 检查手机号是否已注册
    if User.query.filter_by(phone=phone).first():
        return jsonify(code=400, msg='手机号已注册'), 400

    # 3. 限流控制（1分钟内不可重复发送）
    if redis_client.get(f'sms_limit:{phone}'):
        return jsonify(code=429, msg='操作过于频繁'), 429
    redis_client.setex(f'sms_limit:{phone}', 60, '1')

    # 4. 生成并存储验证码
    # code = str(random.randint(100000, 999999))
    code = "666666"
    redis_client.setex(f'sms_code:{phone}', 300, code)

    # 5. 异步发送短信（Celery任务）
    async_send_sms.delay(phone, code)

    return jsonify(code=200, msg='验证码已发送')


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""发送邮箱验证码（用户注册）"""


@app.route('/api/register/send_email', methods=['POST'])
def send_register_email():
    email = request.json.get('email')

    # 1. 校验邮箱格式
    if not re.match(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
                    email):
        return jsonify(code=400, msg='邮箱格式错误'), 400

    # 2. 检查邮箱是否已注册
    if User.query.filter_by(email=email).first():
        return jsonify(code=400, msg='邮箱已注册'), 400

    # 3. 限流控制（1分钟内不可重复发送）
    if redis_client.get(f'email_limit:{email}'):
        return jsonify(code=429, msg='操作过于频繁'), 429
    redis_client.setex(f'email_limit:{email}', 60, '1')

    # 4. 生成并存储验证码
    # code = str(random.randint(100000, 999999))
    code = "888888"
    redis_client.setex(f'email_code:{email}', 300, code)

    # 5. 异步发送邮件（Celery任务）
    async_send_email.delay(email, code)

    return jsonify(code=200, msg='验证码已发送')


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""提交注册信息"""


@app.route('/api/register/submit', methods=['POST'])
def register_submit():
    data = request.json
    phone = data.get('phone')
    sms_code = data.get('sms_code')
    email = data.get('email')
    email_code = data.get('email_code')
    password = data.get('password')
    confirm_password = data.get('confirm_password')

    # 1. 校验手机格式
    if not re.match(r'^1[3-9]\d{9}$', phone):
        return jsonify(code=400, msg='手机号格式错误'), 400

    # 2.校验手机验证码
    stored_sms_code = redis_client.get(f'sms_code:{phone}')
    logger.info("手机号{} 验证码对比: stored_sms_code={}, sms_code={}".format(
        phone, stored_sms_code, sms_code))  # str.format()
    if not stored_sms_code or stored_sms_code.decode() != sms_code:
        return jsonify(code=400, msg='短信验证码错误'), 400

    # 2. 校验邮箱格式
    if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):
        return jsonify(code=400, msg='邮箱格式错误'), 400

    # 3. 校验邮箱验证码
    stored_email_code = redis_client.get(f'email_code:{email}')
    if not stored_email_code or stored_email_code.decode() != email_code:
        return jsonify(code=400, msg='邮箱验证码错误'), 400

    # 4. 校验密码一致性
    if password != confirm_password:
        return jsonify(code=400, msg='两次输入密码不一致'), 400

    # 5. 校验密码复杂度（至少8位，含字母和数字）
    if not re.match(r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$', password):
        return jsonify(code=400, msg='密码需至少8位且包含字母和数字'), 400

    # 6. 创建用户
    password_hash = bcrypt.hashpw(password.encode(),
                                  bcrypt.gensalt()).decode('utf-8')
    logger.info("手机号{} 密码哈希: password_hash={}".format(phone, password_hash))
    user = User(
        phone=phone,
        password_hash=password_hash,
        email=email,
    )
    db.session.add(user)
    db.session.flush()
    db.session.commit()  # 一定要确保提交，这样数据库才会更新

    mindcare_system["memory_system"]._create_user_data(user_id=user.id)

    # 7. 生成JWT令牌（自动登录）
    access_token = create_access_token(identity=user.id)
    refresh_token = create_refresh_token(identity=user.id)

    # 8. 清理验证码
    redis_client.delete(f'sms_code:{phone}', f'email_code:{email}')

    return jsonify(
        code=200,
        msg='注册成功',  # 注册成功后自动登录，返回JWT令牌
        data={
            'access_token': access_token,
            'refresh_token': refresh_token
        })


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""获取用户信息"""


@app.route("/api/profile", methods=["GET"])
@jwt_required()
def get_profile():
    # 从JWT获取用户ID
    user_id = get_jwt_identity()
    logger.info("用户id{}".format(user_id))  # str.format()
    # 查询用户档案
    user = db.session.get(User, user_id)
    if not user:
        return jsonify(code=404, msg="用户不存在"), 404

    # 构造返回数据
    profile_data = {
        "avatar_url": user.avatar_url or "default_avatar.png",
        "nickname": user.nickname or "",
        "gender": user.gender or "",
        "signature": user.signature or "这个人很懒，什么都没留下。"
    }

    return jsonify(code=200, data=profile_data)


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""更新用户信息"""


@app.route("/api/profile", methods=["PUT"])
@jwt_required()
def update_profile():
    user_id = get_jwt_identity()
    data = request.json

    # 数据验证
    if not all(key in ["nickname", "gender", "signature"]
               for key in data.keys()):
        return jsonify(code=400, msg="非法字段"), 400

    # 更新数据库
    try:
        user = User.query.get(user_id)
        for key, value in data.items():
            setattr(user, key,
                    value.strip() if isinstance(value, str) else value)
        db.session.commit()
        return jsonify(code=200, msg="保存成功")
    except Exception as e:
        db.session.rollback()
        return jsonify(code=500, msg="保存失败"), 500


#-----------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------------------------------------
"""上传头像接口"""
UPLOAD_FOLDER = 'static/avatar'
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)


def allowed_file(filename):
    ALLOWED_EXTENSIONS = {'jpg', 'png'}
    return '.' in filename and filename.rsplit(
        '.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route("/api/upload_avatar", methods=["POST"])
@jwt_required()
def upload_avatar():
    user_id = get_jwt_identity()
    file = request.files.get('avatar')

    # 文件校验
    if not file or file.filename == '':
        return jsonify(code=400, msg="未选择文件"), 400
    if not allowed_file(file.filename):
        return jsonify(code=400, msg="仅支持JPG/PNG格式"), 400

    # 生成文件名
    filename = f"{user_id}.jpg"
    # 构建头像路径
    avatar_url = f"{UPLOAD_FOLDER}/{filename}"
    file.save(avatar_url)

    # 更新 user_profile 表
    user = db.session.get(User, user_id)
    # user = User.query.filter_by(id=user_id).first()
    if not user:
        return jsonify(code=404, msg="未找到用户资料记录，无法更新头像路径"), 404
    user.avatar_url = avatar_url
    try:
        db.session.commit()
        return jsonify(code=200,
                       data={"avatar_url": avatar_url},
                       msg="头像上传成功，用户资料已更新")
    except Exception as e:
        db.session.rollback()
        return jsonify(code=500, msg=f"更新用户资料时出错: {str(e)}"), 500


#-----------------------------------------------------------------------------------------------------------------------
"""情绪记录"""


# 1. 提交情绪记录
@app.route("/api/upload_mood", methods=["POST"])
@jwt_required()
def upload_mood():
    user_id = get_jwt_identity()
    data = request.json or {}
    logger.info(f"数据{data}")

    new_record = MoodRecord(user_id=user_id,
                            category=data.get("category"),
                            emotion=data.get("emotion"),
                            date=data.get("date")
                            or datetime.now(timezone.utc),
                            content=data.get("content", ""),
                            summary=data.get("summary", ""),
                            image_url="")

    try:
        db.session.add(new_record)
        db.session.commit()
        return jsonify(code=200,
                       msg="情绪记录上传成功",
                       data={"record_id": str(new_record.id)})
    except Exception as e:
        db.session.rollback()
        logger.error(f"情绪记录上传失败: {str(e)}", exc_info=True)
        return jsonify(code=500, msg="上传情绪记录时发生内部错误"), 500


# 为情绪记录生成一段摘要
@app.route("/api/mood/summary", methods=["POST"])
@jwt_required()  # 添加 JWT 验证装饰器
def generate_mood_record_summary():
    user_id = get_jwt_identity()

    try:
        data = request.json or {}
        category = data.get("category")
        emotion = data.get("emotion")
        content = data.get("content")
        if not category or not emotion or not content:
            logging.error("缺少必要的字段: category, emotion, content")
            return jsonify(code=400, msg="缺少必要的字段"), 400

        # 构建提示词
        prompt = """
作为情绪陪伴助手，请对用户的情绪记录进行多维度解析：
1. 内容摘要：用20字概括主要事件
2. 情绪洞察：分析情绪产生的可能原因（如触发事件、潜在需求）
3. 共鸣回应：结合情绪类型提供共情性话语
4. 行动建议：给出1条轻量级调节建议

输入信息：
- 类别：{category}
- 情绪：{emotion}
- 内容：{content}
"""
        # 生成摘要
        summary = mindcare_system["llm_service"].generate_response(
            prompt=prompt,
            system_message="作为情绪陪伴助手，你需要根据用户的情绪记录生成逻辑清晰、重点突出的摘要。",
            max_tokens=800,
            temperature=0.7)
        return jsonify(code=200, summary=summary)
    except Exception as e:
        logging.error(f"生成摘要时发生错误: {e}", exc_info=True)
        return jsonify(code=500, msg="生成摘要时发生内部错误"), 500


# 2. 获取情绪历史记录（分页）
@app.route("/api/mood/history", methods=["GET"])
@jwt_required()
def get_mood_history():
    user_id = get_jwt_identity()
    page = request.args.get("page", 1, type=int)
    per_page = 10

    # 按日期倒序分页查询
    records = MoodRecord.query.filter_by(user_id=user_id) \
        .order_by(MoodRecord.date.desc()) \
        .paginate(page=page, per_page=per_page)

    # 构造返回数据
    data = [{
        "id":
        r.id,
        "category":
        r.category,
        "emotion":
        r.emotion,
        "date":
        r.date.isoformat(),
        "content":
        r.content[:50] + "..." if len(r.content) > 50 else r.content
    } for r in records.items]

    return jsonify(code=200, data=data)


# 3. 获取记录详情
@app.route("/api/mood/<uuid:record_id>", methods=["GET"])
@jwt_required()
def get_mood_detail(record_id):
    user_id = get_jwt_identity()
    record = MoodRecord.query.filter_by(id=record_id, user_id=user_id).first()

    if not record:
        return jsonify(code=404, msg="记录不存在"), 404

    return jsonify(
        code=200,
        data={
            "category": record.category,
            "emotion": record.emotion,
            "date": record.date.isoformat(),
            "content": record.content,
            "summary": record.summary,
            #    "image_url": record.image_url
        })


# 4. 编辑(更新)/删除记录
@app.route("/api/mood/<uuid:record_id>", methods=["PUT", "DELETE"])
@jwt_required()
def update_mood(record_id):

    user_id = get_jwt_identity()
    record = MoodRecord.query.filter_by(id=record_id, user_id=user_id).first()

    if not record:
        return jsonify(code=404, msg="记录不存在"), 404

    if request.method == "PUT":
        data = request.json or {}
        # 更新各个字段，如有对应字段就更新，否则保持原值
        if "category" in data:
            record.category = data.get("category")
        if "emotion" in data:
            record.emotion = data.get("emotion")
        if "content" in data:
            record.content = data.get("content")
        if "summary" in data:
            record.summary = data.get("summary")
        if "image_url" in data:
            record.image_url = data.get("image_url")
        # 对日期字段进行更新，需要确保传入的日期格式正确
        if "date" in data:
            date_str = data.get("date")
            try:
                # 这里只是示例，要求前端传入 ISO 格式的字符串
                record.date = datetime.fromisoformat(date_str)
            except Exception as e:
                logger.error(f"解析日期错误: {str(e)}", exc_info=True)
                return jsonify(code=400, msg="日期格式错误"), 400
        try:
            db.session.commit()
            return jsonify(code=200, msg="更新成功")
        except Exception as e:
            db.session.rollback()
            logger.error(f"更新记录失败: {str(e)}", exc_info=True)
            return jsonify(code=500, msg="更新记录时发生内部错误"), 500
        print('')
    elif request.method == "DELETE":
        db.session.delete(record)
        db.session.commit()

    return jsonify(code=200, msg="操作成功")


# def validate_emotion(emotion):
#     allowed_categories = ["积极", "消极"]
#     allowed_subtypes = {"积极": ["快乐", "平静", "兴奋"], "消极": ["悲伤", "焦虑", "愤怒"]}
#     return emotion.get("category") in allowed_categories and \
#            emotion.get("subtype") in allowed_subtypes.get(emotion["category"], [])


def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in {'png', 'jpg', 'jpeg'}


#----------------------------------------------------------------------------------
"""获取历史会话记录分页"""


@app.route("/api/conversation/history", methods=["GET"])
@jwt_required()
def get_conversation_history():
    user_id = get_jwt_identity()
    page = request.args.get("page", 1, type=int)
    per_page = 10

    # 按日期倒序分页查询
    # 从 MemorySystem 中获取用户全部的会话记录（字典形式）
    conversation_history = mindcare_system[
        "memory_system"]._get_user_conversation_history(user_id)
    # conversation_history 是一个字典，键为 conversation_id，值为会话上下文字典
    conv_list = []
    for conv_id, context in conversation_history.items():
        # 假设每个上下文中包含 created_at 或 last_updated_utc 信息
        context["conversation_id"] = conv_id
        conv_list.append(context)

    # 以最近更新时间（或创建时间）倒序排序
    conv_list.sort(key=lambda x: x.get("last_updated"), reverse=True)

    # 分页处理
    total = len(conv_list)
    start = (page - 1) * per_page
    end = start + per_page
    page_items = conv_list[start:end]

    return jsonify(code=200,
                   data={
                       "conversations": page_items,
                       "total": total,
                       "page": page,
                       "per_page": per_page
                   })


# 3. 获取会话记录详情
@app.route("/api/conversation/<conversation_id>", methods=["GET"])
@jwt_required()
def get_conversation_detail(conversation_id):
    user_id = get_jwt_identity()
    conversation = mindcare_system["memory_system"].get_conversation_context(
        user_id, conversation_id)

    if not conversation:
        return jsonify(code=404, msg="记录不存在"), 404

    return jsonify(code=200, data=conversation)


#-----------------------------------------------------------------------------------------------------------------------


@celery.task
def async_upload(file, filename):
    upload_to_s3(file, filename)


#-----------------------------------------------------------------------------------------------------------------------
"""错误处理"""


@app.errorhandler(400)
def handle_bad_request(e):
    return jsonify(code=400, msg='请求参数错误'), 400


@app.errorhandler(429)
def rate_limit_exceeded(e):
    return jsonify(code=429, msg='操作过于频繁'), 429


"""在JWT配置后添加刷新令牌接口"""


@app.route('/api/refresh', methods=['POST'])
@jwt_required(refresh=True)
def refresh_token():
    try:
        current_user = get_jwt_identity()
        redis_client.setex(f"user:active:{current_user}", 1800,
                           datetime.now().isoformat())
        new_access_token = create_access_token(identity=current_user)

        return jsonify({
            "code": 200,
            "msg": "令牌刷新成功",
            "data": {
                "access_token": new_access_token
            }
        })
    except Exception as e:
        logger.error(f"令牌刷新失败: {str(e)}")
        return jsonify({"code": 401, "msg": "刷新令牌无效或已过期"}), 401


#-----------------------------------------------------------------------------------------------------------------------
# 主程序入口
if __name__ == '__main__':
    # 从 SETTINGS 获取主机和端口
    host = SETTINGS.host
    print(host)
    port = SETTINGS.port
    debug_mode = SETTINGS.debug_mode
    logger.info(f"启动 Flask Web 应用，地址: http://{host}:{port}，调试模式: {debug_mode}")
    # 注意：生产环境不应使用 debug=True

    app.run(host=host, port=port, debug=debug_mode)
    # app.run(host='0.0.0.0', port=5000, debug=False)  # 关键！必须设置host为0.0.0.0
